﻿using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;

namespace Devonline.AspNetCore;

/// <summary>
/// 附加信息类服务
/// 每个 TEntitySet 类型中的 TAdditional 类型的附加信息认为有且仅有一个
/// </summary>
/// <typeparam name="TDbContext">数据库上下文</typeparam>
/// <typeparam name="TEntitySet">业务数据类型</typeparam>
/// <typeparam name="TAdditional">附加信息数据类型</typeparam>
/// <typeparam name="TKey">键类型</typeparam>
public class DataWithAdditionalService<TDbContext, TEntitySet, TAdditional, TKey> :
    DataService<TDbContext, TEntitySet, TKey>,
    IDataWithAdditionalService<TDbContext, TEntitySet, TAdditional, TKey>
    where TDbContext : DbContext
    where TEntitySet : class, IEntitySet<TKey>
    where TAdditional : class, IAdditional<TKey>
    where TKey : IConvertible
{
    public DataWithAdditionalService(
        TDbContext context,
        ILogger<DataWithAdditionalService<TDbContext, TEntitySet, TAdditional, TKey>> logger,
        IDistributedCache cache,
        IHttpContextAccessor httpContextAccessor,
        AppSetting appSetting
        ) : base(context, logger, cache, httpContextAccessor, appSetting) { }

    #region 业务附加信息类操作方法
    /// <summary>
    /// 新增业务数据的附加信息
    /// </summary>
    /// <param name="additionals">附加数据集合</param>
    /// <param name="additionalId">业务数据主键</param>
    /// <returns></returns>
    public virtual async Task AddAdditionalsAsync(IEnumerable<TAdditional>? additionals, TKey additionalId)
    {
        if (additionals != null && additionals.Any())
        {
            foreach (var entity in additionals)
            {
                entity.AdditionalId = additionalId;
                Create(entity);
                await _context.AddAsync(entity);
            }
        }
    }
    /// <summary>
    /// 更新业务数据的附加信息
    /// </summary>
    /// <param name="additionals">附加数据集合</param>
    /// <param name="additionalId">业务数据主键</param>
    /// <returns></returns>
    public virtual async Task UpdateAdditionalsAsync(IEnumerable<TAdditional>? additionals, TKey additionalId)
    {
        var news = additionals ?? new List<TAdditional>();
        var olds = await GetQueryable<TAdditional>().Where(x => x.AdditionalId != null && x.AdditionalId.Equals(additionalId)).ToListAsync() ?? new List<TAdditional>();
        foreach (var entity in news)
        {
            var old = olds.FirstOrDefault(x => x.Id != null && x.Id.Equals(entity.Id));
            if (old == null)
            {
                //新的有旧的没有, 则新增
                entity.AdditionalId = additionalId;
                Create(entity);
                _context.Add(entity);
            }
            else
            {
                //新的有旧的也有, 则更新
                old.State = entity.State;
                old.Index = entity.Index;
                old.Key = entity.Key;
                old.Value = entity.Value;
                Update(entity);
                _context.Update(old);
            }
        }

        foreach (var old in olds)
        {
            if (!news.Any(x => x.Id != null && x.Id.Equals(old.Id)))
            {
                _context.Remove(old);
            }
        }
    }
    /// <summary>
    /// 删除业务数据的附加信息
    /// </summary>
    /// <param name="additionalId">业务数据主键</param>
    /// <returns></returns>
    public virtual async Task DeleteAdditionalsAsync(TKey additionalId)
    {
        var additionals = await GetQueryable<TAdditional>().Where(x => x.AdditionalId != null && x.AdditionalId.Equals(additionalId)).ToListAsync();
        if (additionals != null && additionals.Count > 0)
        {
            _context.RemoveRange(additionals);
        }
    }
    #endregion

    #region 重载的增删改方法
    /// <summary>
    /// 重载的新增方法会同时将当前类型的附加信息一并新增
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <returns></returns>
    public override async Task<TEntitySet> AddAsync(TEntitySet entitySet)
    {
        var autoSaveChanges = AutoSaveChanges;
        AutoSaveChanges = false;

        await base.AddAsync(entitySet);
        var additionals = GetCollectionMembers<TAdditional>(entitySet);
        if (additionals != null)
        {
            await AddAdditionalsAsync(additionals, entitySet.Id);
        }

        AutoSaveChanges = autoSaveChanges;
        if (AutoSaveChanges)
        {
            await SaveChangesAsync();
        }

        return entitySet;
    }
    /// <summary>
    /// 重载的更新方法会同时将当前类型的附加信息一并更新
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <returns></returns>
    public override async Task<TEntitySet> UpdateAsync(TEntitySet entitySet)
    {
        var autoSaveChanges = AutoSaveChanges;
        AutoSaveChanges = false;

        entitySet = await base.UpdateAsync(entitySet);
        var additionals = GetCollectionMembers<TAdditional>(entitySet);
        if (additionals != null)
        {
            await UpdateAdditionalsAsync(additionals, entitySet.Id);
        }

        AutoSaveChanges = autoSaveChanges;
        if (AutoSaveChanges)
        {
            await SaveChangesAsync();
        }

        return entitySet;
    }
    /// <summary>
    /// 删除记录及其附属信息
    /// </summary>
    /// <param name="additionalId">业务数据主键</param>
    /// <returns></returns>
    public override async Task DeleteAsync(TKey additionalId)
    {
        var autoSaveChanges = AutoSaveChanges;
        AutoSaveChanges = false;

        await DeleteAdditionalsAsync(additionalId);
        await base.DeleteAsync(additionalId);

        AutoSaveChanges = autoSaveChanges;
        if (AutoSaveChanges)
        {
            await SaveChangesAsync();
        }
    }
    #endregion
}

/// <summary>
/// 附加信息类服务
/// 字符串作为主键的默认实现
/// 每个 TEntitySet 类型中的 TAdditional 类型的附加信息认为有且仅有一个
/// </summary>
/// <typeparam name="TDbContext">数据库上下文</typeparam>
/// <typeparam name="TEntitySet">业务数据类型</typeparam>
/// <typeparam name="TAdditional">附加信息数据类型</typeparam>
public class DataWithAdditionalService<TDbContext, TEntitySet, TAdditional> :
    DataWithAdditionalService<TDbContext, TEntitySet, TAdditional, string>,
    IDataWithAdditionalService<TDbContext, TEntitySet, TAdditional>
    where TDbContext : DbContext
    where TEntitySet : class, IEntitySet<string>
    where TAdditional : class, IAdditional<string>
{
    public DataWithAdditionalService(
        TDbContext context,
        ILogger<DataWithAdditionalService<TDbContext, TEntitySet, TAdditional, string>> logger,
        IDistributedCache cache,
        IHttpContextAccessor httpContextAccessor,
        AppSetting appSetting
        ) : base(context, logger, cache, httpContextAccessor, appSetting) { }
}